package com.iqik.filter.tp_monitor;

import com.iqik.filter.bean.MethodInfo;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.*;

//@Activate(group = CommonConstants.PROVIDER)
public class TPMonitorFilter implements Filter, Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(TPMonitorFilter.class);

    private static Map<String, List<MethodInfo>> METHOD_LIST_MAP = new ConcurrentHashMap<>();

    public TPMonitorFilter() {
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(this, 5, 5, TimeUnit.SECONDS);
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(new RemoveMethodList(), 60, 60, TimeUnit.SECONDS);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        Result result = null;

        long startTime = System.currentTimeMillis();
        result = invoker.invoke(invocation);
        long takeTime = System.currentTimeMillis() - startTime;

        String methodName = invocation.getMethodName();
        List<MethodInfo> methodInfos = METHOD_LIST_MAP.get(methodName);
        if (methodInfos == null) {
            methodInfos = new ArrayList<>();
            METHOD_LIST_MAP.put(methodName, methodInfos);
        }
        MethodInfo methodInfo = new MethodInfo(startTime, takeTime, methodName);
        methodInfos.add(methodInfo);
        return result;
    }

    private long getTP(List<MethodInfo> methodInfos, double rate) {
        if (methodInfos.size() <= 0)
            return -9999;
        try {
            List<MethodInfo> sortInfo = new ArrayList<>();
            long endTime = System.currentTimeMillis();
            long startTime = endTime - 60000;
            for (MethodInfo methodInfo : methodInfos) {
                if (methodInfo.getStartTime() >= startTime && methodInfo.getEndTime() <= endTime) {
                    sortInfo.add(methodInfo);
                }
            }
            sortInfo.sort((o1, o2) -> {
                return (int) (o1.getTakeTime() - o2.getTakeTime());
            });
            int tp = (int) Math.ceil(sortInfo.size() * rate);
            tp = tp == methodInfos.size() ? tp - 1 : tp;
            return sortInfo.get(tp).getTakeTime();
        } catch (Exception e) {
            LOGGER.error("没有消费端调用接口");
            return -9999;
        }
    }

    @Override
    public void run() {
        for (Map.Entry<String, List<MethodInfo>> entry : METHOD_LIST_MAP.entrySet()) {
            LOGGER.info("方法 -> {} ====> {}", entry.getKey(), entry.getValue().size());
            LOGGER.info("方法 -> {} TP90 : {}", entry.getKey(), getTP(entry.getValue(), 0.9));
            LOGGER.info("方法 -> {} TP99 : {}", entry.getKey(), getTP(entry.getValue(), 0.99));
            LOGGER.info(" ");
        }
    }

    class RemoveMethodList implements Runnable {

        @Override
        public void run() {
            for (Map.Entry<String, List<MethodInfo>> methodInfos : METHOD_LIST_MAP.entrySet()) {
                List<MethodInfo> methodList = methodInfos.getValue();
                Iterator<MethodInfo> iterator = methodList.iterator();
                long con = System.currentTimeMillis() - 60000;
                while (iterator.hasNext()) {
                    if (iterator.next().getEndTime() < con) {
                        iterator.remove();
                    }
                }
            }
        }
    }
}
